﻿using System;
using System.Globalization;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using HighDiveEngine.Aggregates;
using HighDiveEngine.IoEngines;
using HighDiveEngine.Model;
using HighDiveEngine.Resources;

namespace HighDiveAdmin.Handlers
{
    public class TcpHandler
    {
        #region Properties

        private TcpEngine _tcpEngine;
        private Thread _serverThread;
        private string _ipAddress ;

        private static readonly TcpHandler ThisInstance = new TcpHandler();

        //admin application events
        public static event DelegateVoidStringString EventJudgeLoginAttempt;
        public static event DelegateVoidString EventJudgeLogout;
        public static event DelegateVoidDiveScoreDive EventAdminDiveScoreReceived;
        public static event DelegateVoidString EventConnectionFailed;
        public static event DelegateVoidString EventJudgeIsAlive;
        

        public string IpAddress
        {
            get { return _ipAddress; }
            set
            {
                if (_serverThread != null && _serverThread.IsAlive)
                {
                    _serverThread.Abort();
                    _serverThread.Join();
                }
                    
                _ipAddress = value;
                _tcpEngine = new TcpEngine(value, ReceiveMessage, _connectionFailed);
                _serverThread = new Thread(_tcpEngine.TcpServerThread) { IsBackground = true };
                _serverThread.Start();
            }
        }

        public static TcpHandler Instance { get { return ThisInstance; } }

        #endregion

        #region Constructor
        private TcpHandler()
        {
            _setIp();
            //IpAddress = "127.0.0.1:9059";
        }

        #endregion

        #region Private Methods
        private void _setIp()
        {
            string localIp = "?";
            var host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily.ToString() == "InterNetwork")
                {
                    localIp = ip + ":9059";
                }
            }
            IpAddress = localIp;
        }

        private void _connectionFailed(string ip)
        {
            if (EventConnectionFailed != null)
                EventConnectionFailed(ip);
        }

        #endregion

        #region Send Methods
        public void SendMessage(string message)
        {
            _tcpEngine.TcpTxMessage(message);
        }

        public void SendContestEnded(Judge judge)
        {
            _tcpEngine.TcpTxMessage(judge.IpAdress + "@AdminContestEnded");
        }

        public void SendNewDiveToJudge(JudgeList judgeList, Dive dive)
        {
            foreach (Judge judge in judgeList)
            {
                lock (judge.Status)
                {
                    if (judge.Status.Equals(JudgeStatus.Status.Connected) || judge.Status.Equals(JudgeStatus.Status.SlowReply))
                    {
                        _tcpEngine.TcpTxMessage(judge.IpAdress + "@AdminNewDive:" + dive.Diver.IdNumber + ":" + dive.Diver.FirstName + ":" +
                                                        dive.Diver.LastName + ":" + dive.Diver.StartNumber + ":" + dive.DiveNumber +
                                                        ":" + dive.DiveCode + ":" + dive.DiveHeight + ":" + Convert.ToDouble(dive.Difficulty).ToString(CultureInfo.InvariantCulture));
                    } 
                }
            }
        }

        public void SendUpdatedScoreToJudge(Judge judge, Dive dive)
        {
            lock (judge.Status)
            {
                if (judge.Status.Equals(JudgeStatus.Status.Connected) || judge.Status.Equals(JudgeStatus.Status.SlowReply))
                {
                    _tcpEngine.TcpTxMessage(judge.IpAdress + "@AdminUpdateScore:" + dive.Diver.IdNumber + ":" +
                                            dive.Diver.FirstName +
                                            ":" + dive.Diver.LastName + ":" + dive.Diver.StartNumber + ":" +
                                            dive.DiveNumber +
                                            ":" + dive.DiveCode + ":" + dive.DiveHeight + ":" + Convert.ToDouble(dive.Difficulty).ToString(CultureInfo.InvariantCulture) +
                                            ":" +
                                            dive.DiveScoreList.Find(x => x.Judge.Equals(judge)).Judge.IdString + ":" +
                                            dive.DiveScoreList.Find(x => x.Judge.Equals(judge)).Score.ToString(CultureInfo.InvariantCulture));
                }
            }
        }

        public void SendLoginSuccessfulToJudge(Judge judge)
        {
            _tcpEngine.TcpTxMessage(judge.IpAdress + "@AdminLoginSuccessful");
        }

        public void SendLoginRefusedToIp(string ipAdress)
        {
            _tcpEngine.TcpTxMessage(ipAdress + "@AdminLoginRefused");
        }

        #endregion

        #region Receive Methods
        public void ReceiveMessage(string message)
        {
            if (new Regex(@"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]{4,5}@.*$").IsMatch(message))
            {
                string[] msg = message.Split('@');

                if (new Regex(@"^JudgeAppLogin:[a-zA-Z0-9]{5}:[0-9]{4,5}$").IsMatch(msg[1]))
                {
                    receiveJudgeLogin(msg);
                }
                else if (new Regex(@"^JudgeAppLogout:[a-zA-Z0-9]{5}$").IsMatch(msg[1]))
                {
                    receiveJudgeLogout(msg);
                }
                else if (new Regex(@"^JudgeAppUpdateScore:[a-zA-Z0-9]{5}:[0-9]{10}:[0-9]*:[0-9]*:[0-9]{3,4}[a-zA-Z]:([0-9]([\.|\,][0-9])?|[1][0])$").IsMatch(msg[1]))
                {
                    receiveJudgeUpdateScore(msg);
                } 
                else if (new Regex(@"^JudgeAppIsAlive:[a-zA-Z0-9]{5}$").IsMatch(msg[1]))
                {
                    receiveJudgeIsAlive(msg);
                }
            }
            else
            {
                throw new TcpHandlerException("Unknown message received");
            }
        }

        private void receiveJudgeLogout(string[] msg)
        {
            string[] splitmsg = msg[1].Split(':');
            if (EventJudgeLogout != null)
                EventJudgeLogout(splitmsg[1]);
        }

        private void receiveJudgeIsAlive(string[] msg)
        {
            string[] splitmsg = msg[1].Split(':');
            if (EventJudgeIsAlive != null)
                EventJudgeIsAlive(splitmsg[1]);
        }

        private void receiveJudgeUpdateScore(string[] msg)
        {
            string[] splitmsg = msg[1].Split(':');
            DiveScore score = new DiveScore()
            {
                Judge = new Judge {IdString = splitmsg[1], IpAdress = msg[0]},
                Score = Convert.ToDouble(splitmsg[6], CultureInfo.InvariantCulture)
            };
            Dive dive = new Dive()
            {
                Diver = new Diver() {IdNumber = splitmsg[2], StartNumber = Convert.ToInt32(splitmsg[3])},
                DiveNumber = Convert.ToInt32(splitmsg[4]),
                DiveCode = splitmsg[5],
            };

            if (EventAdminDiveScoreReceived != null)
                EventAdminDiveScoreReceived(score, dive);
        }

        private void receiveJudgeLogin(string[] msg)
        {
            string[] splitmsg = msg[1].Split(':');
            if (EventJudgeLoginAttempt != null)
                EventJudgeLoginAttempt(msg[0].Split(':')[0] + ":" + splitmsg[2], splitmsg[1]);
        }

        #endregion
    }
}
